/*
 * authmgr.cpp
 *
 *  Created on: Jun 18, 2014
 *      Author: killerg
 */

#include "auth_mgr.h"
#include "auth_cfg.h"
#include "auth.h"
#include "share/clock.h"

void auth_mgr_t::auth_91(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	const char* curl = "http://sdk.m.duoku.com/openapi/sdk/checksession";

	static const int AppId = 3431355;
	static const char* ProductKey = "5GNsQZKN3C2XsxPl48a0PD3i";
	static const char* ProductSecret = "nLVoerlIMcaKLsu2hA44AHkD0ncIaCrx";

	if (!json["uid"].isString() || !json["sessionid"].isString()) {
		account_t account;
		cb(AuthFail, account, Json::nullValue);
		return;
	}

	string uid = json["uid"].asCString();
	string sessionid = json["sessionid"].asCString();
	string clientsecret = Md5(
			fmt_cstr("%d%s%s%s%s", AppId, ProductKey, uid.c_str(),
					sessionid.c_str(), ProductSecret));

	string url = fmt_cstr(
			"%s?appid=%d&appkey=%s&uid=%s&sessionid=%s&clientsecret=%s", curl,
			AppId, ProductKey, uid.c_str(), sessionid.c_str(),
			clientsecret.c_str());

	mem_log("auth_91 start uid:%s url:%s", uid.c_str(), url.c_str());

	g_auth.authmgr->htpc.do_query(url.c_str(),
	NULL,
			[this, cb, uid](CURLcode code, const std::string& data) {
				mem_log("auth_91 finish uid:%s", uid.c_str());

				if (CURLE_OK != code) {
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}

				Json::Value ret_json = to_json(data);
				if (!ret_json.isObject()) {
					UTIL_LOG(E, "err http_req of auth_91:%s", data.c_str());
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}

				const string error_code = ret_json["error_code"].asCString();
				const string error_msg = ret_json["error_msg"].asCString();
				if (error_code != "0") {
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}

				string account = fmt_cstr("91-%s", uid.c_str());
				get_account(account, [this, account, cb](result_t res, const account_t &taccount) {
							if (res == Exist) {
								cb(Ok, taccount, Json::nullValue);
							} else if (res == Noexist) {
								account_t taccount;
								taccount.account = account;
								taccount.platform = "91";
								taccount.playerid = this->allocid();
								taccount.create = g_clock->tt_now;
								add_account(taccount, [taccount, cb](result_t res) {
											if (res == Created) {
												cb(Ok, taccount, Json::nullValue);
											} else
											cb(res, taccount, Json::nullValue);
										});
							} else {
								cb(TmpFail, taccount, Json::nullValue);
							}
						});

			});
}

void auth_mgr_t::auth_mi(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}
//
//	static const char* appId = "23752";
//	static const char* appKey = "71f76a0c-b11f-6807-16bb-5305f2d0685a";
//
//    if (!auth_data.isObject()) {
//        cb(-1, "", "", "");
//        return;
//    }
//
//    if (!auth_data["session"].isString() || !auth_data["uid"].isString() || !auth_data["mc"].isString()) {
//        cb(-1, "", "", "");
//        return;
//    }
//
//    string session = auth_data["session"].asCString();
//    string uid = auth_data["uid"].asCString();
//    string mc = auth_data["mc"].asCString();
//    string param = fmt_cstr("appId=%s&session=%s&uid=%s", appId, session.c_str(), uid.c_str());
//    string sign = Sha1(param.c_str(), appKey);
//    string url = fmt_cstr("http://mis.migc.xiaomi.com/api/biz/service/verifySession.do?%s&signature=%s", param.c_str(), sign.c_str());
////    UTIL_LOG(I, "mi url:%s", url.c_str());
//
//    mem_log("auth_mi start uid:%s url:%s", uid.c_str(), url.c_str());
//    bool res = g_auth.authmgr->http.do_query(
//            url.c_str(),
//            NULL,
//            [auth_data, mc, uid, cb](int code, const string &data){
//                --g_auth.authmgr->httpop_num;
//                mem_log("auth_mi finish uid:%s", uid.c_str());
//
//                const Json::Value json = to_json(data);
//                if (!json.isObject()) {
//                	UTIL_LOG(E, "err http_req of auth_mi:%s", data.c_str());
//                    cb(-1, "", "", "");
//                    return;
//                }
//
//                switch (json["errcode"].asInt()) {
//                case 200: {
//                    string account = fmt_cstr("mi-%s", uid.c_str());
//                    auth(account, mc, cb);
//                    break;
//                }
//                default:
//                    cb(-1, "", "", "");
//                    break;
//                }
//            });
//    if (res)
//        ++g_auth.authmgr->httpop_num;

}

void auth_mgr_t::auth_360(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	if (!json["access_token"].isString()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	string access_token = json["access_token"].asCString();

	const char* url = fmt_cstr("https://openapi.360.cn/user/me?access_token=%s",
			access_token.c_str());
	htpc.do_query(url, NULL,
			[this, cb](CURLcode code, const std::string& data) {
				if (CURLE_OK != code) {
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}

				Json::Value data_json = to_json(data);
				if (!data_json["error_code"].isNull() || !data_json["id"].isString()) {
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}

				string account = fmt_cstr("360-%s", data_json["id"].asCString());

				get_account(account, [this, account, cb](result_t res, const account_t &taccount) {
							if (res == Exist) {
								Json::Value j360;
								Json::Value json;
								j360["id"] = taccount.account;
								json["360"] = j360;
								cb(Ok, taccount, json);
							} else if (res == Noexist) {
								account_t taccount;
								taccount.account = account;
								taccount.platform = "360";
								taccount.playerid = this->allocid();
								taccount.create = g_clock->tt_now;
								add_account(taccount, [taccount, cb](result_t res) {
											if (res == Created) {
												Json::Value j360;
												Json::Value json;
												j360["id"] = taccount.account;
												json["360"] = j360;
												cb(Ok, taccount, json);
											} else
											cb(res, taccount, Json::nullValue);
										});
							} else {
								cb(TmpFail, taccount, Json::nullValue);
							}
						});

			});
}

void auth_mgr_t::auth_uc(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	if (!json["sid"].isString()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	string sid = json["sid"].asCString();

	const char* uc_url = "http://sdk.g.uc.cn/ss/";
	if (g_cfg->Test)
		uc_url = "http://sdk.test4.g.uc.cn/ss/";

	static const char* uc_service = "ucid.user.sidInfo";
	static const char* channelId = "2";
	static int serverId = 3395;
	static int cpId = 40040;
	static int gameId = 542195;
	static const char* apiKey = "dc5b3d722b51f99aa6ec7ffd08a0f8ab";

	uint64_t serial = g_auth.serial++;

	Json::Value body;
	body["id"] = Json::Int64(serial);
	body["service"] = uc_service;

	Json::Value &data_json = body["data"];
	data_json["sid"] = sid;

	Json::Value &game_json = body["game"];
	game_json["cpId"] = cpId;
	game_json["gameId"] = gameId;
	game_json["channelId"] = channelId;
	game_json["serverId"] = serverId;

	body["sign"] = Md5(fmt_cstr("%dsid=%s%s", cpId, sid.c_str(), apiKey));

	htpc.do_query(uc_url, to_cstr(body),
			[this, serial, sid, cb](CURLcode code, const std::string& data) {
				if (CURLE_OK != code) {
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}

				const Json::Value res_json = to_json(data);

				if (!res_json.isObject()) {
					UTIL_LOG(E, "err http_req of auth_uc:%s", data.c_str());
					account_t account;
					cb(AuthFail, account, Json::nullValue);
					return;
				}

				if (res_json["id"].asInt() != serial) {
					account_t account;
					cb(AuthFail, account, Json::nullValue);
					return;
				}

				const Json::Value &state_json = res_json["state"];
				if (1 != state_json["code"].asInt()) {
					account_t account;
					cb(AuthFail, account, Json::nullValue);
					return;
				}

				const Json::Value &date_json = res_json["data"];
				string account = fmt_cstr("uc-%d", date_json["ucid"].asInt());

				get_account(account, [this, account, cb](result_t res, const account_t &taccount) {
							if (res == Exist) {
								Json::Value juc;
								Json::Value json;
								juc["id"] = taccount.account;
								json["uc"] = juc;
								cb(Ok, taccount, json);
							} else if (res == Noexist) {
								account_t taccount;
								taccount.account = account;
								taccount.platform = "uc";
								taccount.playerid = this->allocid();
								taccount.create = g_clock->tt_now;
								add_account(taccount, [taccount, cb](result_t res) {
											if (res == Created) {
												Json::Value juc;
												Json::Value json;
												juc["id"] = taccount.account;
												json["uc"] = juc;
												cb(Ok, taccount, json);
											} else {
												cb(res, taccount, Json::nullValue);
											}
										});
							} else {
								cb(TmpFail, taccount, Json::nullValue);
							}
						});
			});
}

void auth_mgr_t::auth_au(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	if (!json["account"].isString() || !json["passwd"].isString()
			|| !json["create"].isBool()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	string account = fmt_cstr("au-%s", json["account"].asCString());
	string passwd = json["passwd"].asCString();
	bool create = json["create"].asBool();

	get_account(account,
			[create, account, passwd, this, cb](result_t res, const account_t &taccount) {
				if (res == Exist) {
					if (create) {
						cb(Exist, taccount, Json::nullValue);
					} else {
						if (taccount.passwd == passwd) {
							cb(Ok, taccount, Json::nullValue);
						} else {
							cb(AuthFail, taccount, Json::nullValue);
						}
					}
				} else if (res == Noexist) {
					if (create) {
						account_t taccount;
						taccount.account = account;
						taccount.passwd = passwd;
						taccount.platform = "au";
						taccount.playerid = this->allocid();
						taccount.create = g_clock->tt_now;
						add_account(taccount, [taccount, cb](result_t res) {
									if (res == Created)
									cb(Ok, taccount, Json::nullValue);
									else
									cb(res, taccount, Json::nullValue);
								});
					} else {
						cb(Noexist, taccount, Json::nullValue);
					}
				} else {
					cb(TmpFail, taccount, Json::nullValue);
				}
			});
}

void auth_mgr_t::auth_aulj(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	if (!json["account"].isString() || !json["passwd"].isString()
			|| !json["create"].isBool()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	string channel = json["channel"].asCString();
	string account = fmt_cstr("%s-%s", channel.c_str(),
			json["account"].asCString());
	string passwd = json["passwd"].asCString();
	bool create = json["create"].asBool();

	get_account(account,
			[channel, create, account, passwd, this, cb](result_t res, const account_t &taccount) {
				if (res == Exist) {
					if (create) {
						cb(Exist, taccount, Json::nullValue);
					} else {
						if (taccount.passwd == passwd) {
							cb(Ok, taccount, Json::nullValue);
						} else {
							cb(AuthFail, taccount, Json::nullValue);
						}
					}
				} else if (res == Noexist) {
					if (create) {
						account_t taccount;
						taccount.account = account;
						taccount.passwd = passwd;
						taccount.platform = channel;
						taccount.playerid = this->allocid();
						taccount.create = g_clock->tt_now;
						add_account(taccount, [taccount, cb](result_t res) {
									if (res == Created)
									cb(Ok, taccount, Json::nullValue);
									else
									cb(res, taccount, Json::nullValue);
								});
					} else {
						cb(Noexist, taccount, Json::nullValue);
					}
				} else {
					cb(TmpFail, taccount, Json::nullValue);
				}
			});
}

void auth_mgr_t::auth_lj(const Json::Value &json, cb2_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	if (!json["userid"].isString() || !json["channel"].isString()
			|| !json["token"].isString() || !json["productcode"].isString()) {
		account_t account;
		cb(TmpFail, account, Json::nullValue);
		return;
	}

	const char* path_fmt =
			"http://gameproxy.xinmei365.com/game_agent/checkLogin"
					"?userId=%s"
					"&channel=%s"
					"&token=%s"
					"&productCode=%s";
	string path = fmt_cstr(path_fmt, json["userid"].asCString(),
			json["channel"].asCString(), json["token"].asCString(),
			json["productcode"].asCString());

	string platform = json["channel"].asCString();
	string account = fmt_cstr("%s-%s", platform.c_str(),
			json["userid"].asCString());

	htpc.do_query(path.c_str(), NULL,
			[cb, account, platform, this](CURLcode code, const std::string& data) {
				if (CURLE_OK != code) {
					account_t account;
					cb(TmpFail, account, Json::nullValue);
					return;
				}
				if (data != "true") {
					account_t account;
					cb(AuthFail, account, Json::nullValue);
					return;
				}

				get_account(account, [account, platform, this, cb](result_t res, const account_t &taccount) {
							if (res == Exist) {
								cb(Ok, taccount, Json::nullValue);
							} else if (res == Noexist) {
								account_t taccount;
								taccount.account = account;
								taccount.platform = platform;
								taccount.playerid = this->allocid();
								taccount.create = g_clock->tt_now;
								add_account(taccount, [taccount, cb](result_t res) {
											if (res == Created)
											cb(Ok, taccount, Json::nullValue);
											else
											cb(res, taccount, Json::nullValue);
										});
							} else {
								cb(TmpFail, taccount, Json::nullValue);
							}
						});
			});

	return;
}

void auth_mgr_t::get_account(const string &saccount, cb_t cb) {
	if (!db.ok()) {
		account_t account;
		cb(TmpFail, account);
		return;
	}

	auto itr = cache.find(saccount);
	if (itr != cache.end()) {
		cb(Exist, itr->second);
		return;
	}

	if (dealing.count(saccount) > 0) {
		account_t account;
		cb(TmpFail, account);
		return;
	}

	string key = fmt_cstr("account:%s", saccount.c_str());
	bool res = this->db.get(key.c_str(),
			[cb, saccount, this](db_error_t err, const Json::Value &json) {
				dealing.erase(saccount);
				if (err == DBE_NoExist) {
					account_t account;
					cb(Noexist, account);
					return;
				} else if (err == DBE_Success) {
					account_t account;
					account.account = json["account"].asCString();
					account.platform = json["platform"].asCString();
					account.passwd = json["passwd"].asCString();
					account.playerid = json["playerid"].asInt();
					account.create = json["create"].asInt();

					cache[saccount] = account;

					cb(Exist, account);
					return;
				} else {
					account_t account;
					cb(TmpFail, account);
					return;
				}
			});
	if (!res) {
		account_t account;
		cb(TmpFail, account);
		return;
	} else {
		dealing.insert(saccount);
	}

}
void auth_mgr_t::add_account(const account_t &account,
		function<void(result_t)> cb) {
	if (!db.ok()) {
		cb(TmpFail);
		return;
	}

	if (dealing.count(account.account) > 0) {
		cb(TmpFail);
		return;
	}

	int32_t create_time = g_auth.clock->tt_now;
	Json::Value save;
	save["account"] = account.account;
	save["playerid"] = account.playerid;
	save["platform"] = account.platform;
	save["passwd"] = account.passwd;
	save["create"] = account.create;

	string key = fmt_cstr("account:%s", account.account.c_str());
	bool res = this->db.add(key.c_str(), save,
			[create_time, cb, account, this](db_error_t err) {
				dealing.erase(account.account);

				if (err == DBE_Success) {
					cache[account.account] = account;

					cb(Created);
				} else if (err == DBE_Exist) {
					cb(Exist);
				} else {
					cb(TmpFail);
				}
			});
	if (!res) {
		cb(TmpFail);
	} else {
		dealing.insert(account.account);
	}
}

void auth_mgr_t::update() {
	htpc.update();
	db.update();
}

bool auth_mgr_t::init() {
	if (!htpc.init()) {
		return false;
	}
	if (!db.init(g_auth.cfg->DbHost, "", "", g_auth.cfg->DbBucket)) {
		return false;
	}

	bool res = db.get("_info",
			[this, &res](db_error_t err, const Json::Value &json) {
				res = true;
				if (err == DBE_NoExist) {
					this->maxid = -1;
				} else if (err == DBE_Success) {
					this->maxid = json["maxid"].asInt();
				} else {
					res = false;
				}
			});
	db.wait();

	return res;
}

void auth_mgr_t::destroy() {
	Json::Value json;
	json["maxid"] = maxid;

	bool res = false;
	for (int i = 0; !res && i < 10; ++i) {
		res = db.set("_info", json, [&res](db_error_t err) {
			if (err == DBE_Success) {
				res = true;
			} else {
				res = false;
			}
		});
		db.wait();
		if (!res)
			sleep(1);
	}

	db.destroy();
	htpc.destroy();
	cache.clear();
	dealing.clear();
}

static bool test_auth() {
	auth_mgr_t auth;
	assert(auth.init());

	for (int i = 0; i < 100; ++i) {
		auth.update();
		usleep(1000 * 100);
	}

	auth.destroy();

	return true;
}

void auth_mgr_t::onactive(const string &mc, const string &platform,
		function<void(time_t)> cb) {
	string key = mc + ":" + platform;

	if (mcactive.count(key) == 0) {
		get_mcch(key, [this, key, cb](time_t create) {
			if (create > 0) {
				mcactive.insert(make_pair(key, create));
				cb(create);
			} else {
				add_mcch(key, [cb](time_t create) {
							cb(create);
						});
			}
		});
	} else {
		cb(mcactive[key]);
	}
}

void auth_mgr_t::get_mcch(const string &key, function<void(time_t)> cb) {
	string dbkey = fmt_cstr("active:%s", key.c_str());
	g_auth.authmgr->db.get(dbkey.c_str(),
			[cb](db_error_t err, const Json::Value &json) {
				if (err == DBE_Success) {
					time_t active = json["active"].asInt64();
					cb(active);
				} else {
					cb(-1);
				}
			});
}

void auth_mgr_t::add_mcch(const string &key, function<void(time_t)> cb) {
	time_t active = g_clock->tt_now;
	Json::Value json;
	json["objtype"] = "active";
	json["active"] = Json::Int64(active);

	string dbkey = fmt_cstr("active:%s", key.c_str());
	g_auth.authmgr->db.add(dbkey.c_str(), json, [active, cb](db_error_t err) {
		if (err == DBE_Success) {
			cb(active);
		} else {
			cb(-1);
		}
	});
}
/*
 "lj": {
 "userid":xxxx,
 "channel":yyyy,
 "token":zzzz,
 "productcode":aaaa
 }
 "360":{
 "access_token":
 }
 "uc":{
 "sid":
 }
 "au":{
 "account":
 "passwd":
 "create":
 }
 */

/*
 360: {
 id:
 }
 uc:{
 }
 augames:{
 }
 */

void auth_mgr_t::auth(const Json::Value &json, cb2_t cb) {
	if (json.isNull()) {
		account_t account;
		cb(AuthFail, account, Json::nullValue);
	} else if (json.isMember("360")) {
		auth_360(json["360"], cb);
	} else if (json.isMember("uc")) {
		auth_uc(json["uc"], cb);
	} else if (json.isMember("lj")) {
		auth_lj(json["lj"], cb);
	} else if (json.isMember("91")) {
		auth_91(json["91"], cb);
	} else if (json.isMember("au")) {
		auth_au(json["au"], cb);
	} else if (json.isMember("aulj")) {
		auth_aulj(json["aulj"], cb);
	}
}
